package local

import (
	"fmt"
	"gitee.com/gitee-go/core"
	"gitee.com/gitee-go/core/bean/hbtpBean"
	"gitee.com/gitee-go/core/common"
	"gitee.com/gitee-go/core/model/pipeline"
	comm2 "gitee.com/gitee-go/server/comm"
	"gitee.com/gitee-go/server/engine/comm"
	"gitee.com/gitee-go/utils"
	"runtime/debug"
	"strings"
	"time"
)

// 检查任务数据完整性,检查不通过将不会执行流水线
func (c *taskItem) check() bool {
	if c.build.Cfg == nil {
		c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
		c.build.Error = "build config is nil"
		return false
	}
	if c.build.Cfg.Pipe == nil {
		c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
		c.build.Error = "build config.pipe is nil"
		return false
	}
	if c.build.Cfg.Pipe.Stages == nil || len(c.build.Cfg.Pipe.Stages) <= 0 {
		c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
		c.build.Error = "build Stages is empty"
		return false
	}
	for _, v := range c.build.Cfg.Pipe.Stages {
		if v.BuildId != c.build.Id {
			c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
			c.build.Error = fmt.Sprintf("Stage Build id err:%s/%s", v.BuildId, c.build.Id)
			return false
		}
		if v.Name == "" {
			c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
			c.build.Error = "build Stage name is empty"
			return false
		}
		if v.Jobs == nil || len(v.Jobs) <= 0 {
			c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
			c.build.Error = "build Stages is empty"
			return false
		}
		if _, ok := c.stages[v.Name]; ok {
			c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
			c.build.Error = fmt.Sprintf("build Stages.%s is repeat", v.Name)
			return false
		}
		vs := &TaskStage{
			stage: v,
			jobs:  make(map[string]*comm.SyncJob),
		}
		c.stages[v.Name] = vs
		for _, e := range v.Jobs {
			if e.BuildId != c.build.Id {
				c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
				c.build.Error = fmt.Sprintf("Job Build id err:%s/%s", v.BuildId, c.build.Id)
				return false
			}
			if e.StageId != v.Id {
				c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
				c.build.Error = fmt.Sprintf("Job Stage id err:%s/%s", v.BuildId, c.build.Id)
				return false
			}
			if strings.TrimSpace(e.Job) == "" {
				c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
				c.build.Error = "build Step Plugin is empty"
				return false
			}
			if e.Name == "" {
				c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
				c.build.Error = "build Step name is empty"
				return false
			}
			if _, ok := vs.jobs[e.Name]; ok {
				c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
				c.build.Error = fmt.Sprintf("build Job.%s is repeat", e.Name)
				return false
			}
			job := comm.NewSyncJob(e)
			err := c.gencmds(job)
			if err != nil {
				c.build.Event = common.BUILD_EVENT_ERR_CHECK_PARAM
				c.build.Error = fmt.Sprintf("build Job.%s gen command err", e.Name)
				return false
			}
			vs.jobs[e.Name] = job
		}
	}

	return true
}

// 解析命令,把命令加入命令队列
func (c *taskItem) appendcmds(job *comm.SyncJob, grp *comm.CmdGroupJson, conts string) {
	if grp == nil {
		return
	}
	m := &hbtpBean.CmdContent{
		Id:    utils.NewXid(),
		Gid:   grp.Id,
		Conts: conts,
	}
	tm := &pipeline.TCmdLine{
		Id: m.Id,
		//GroupId: grp.Id,
		//BuildId: c.build.Id,
		//JobId: job.Job().Id,
		//Gid: grp.Id,
		Content: conts,
		Created: time.Now(),
	}
	vls := common.RegVar.FindAllStringSubmatch(conts, -1)
	// 脱敏处理,如果变量是敏感的,需要变成***
	for _, zs := range vls {
		k := zs[1]
		if k == "" {
			continue
		}
		if job.Job().PlugVars != nil {
			va, ok := job.Job().PlugVars[k]
			if ok {
				m.Conts = strings.ReplaceAll(m.Conts, zs[0], va)
				tm.Content = strings.ReplaceAll(tm.Content, zs[0], va)
			}
		}
		va, ok := c.build.Vars[k]
		if ok {
			m.Conts = strings.ReplaceAll(m.Conts, zs[0], va.Value)
			if va.Secret {
				tm.Content = strings.ReplaceAll(tm.Content, zs[0], "***")
			} else {
				tm.Content = strings.ReplaceAll(tm.Content, zs[0], va.Value)
			}
		}
	}
	job.Runjb().Commands = append(job.Runjb().Commands, m)
	grp.Cmds = append(grp.Cmds, tm)
}

/**
解析yaml生成命令结构
job的commands格式是[]interface,每一个元素type格式不确定,但是只能是下面几种
1. string
2. []string
3. map[string]string
4. map[string][]string
所以需要用switch分别处理不用的type
*/
func (c *taskItem) gencmds(job *comm.SyncJob) (rterr error) {
	defer func() {
		if err := recover(); err != nil {
			core.LogPnc.Errorf("cmdExec gencmds:%+v", err)
			core.LogPnc.Errorf("%s", string(debug.Stack()))
			rterr = fmt.Errorf("%v", err)
		}
	}()
	ln := 0
	for _, v := range job.Job().Commands {
		switch v.(type) {
		case string:
			grp := &comm.CmdGroupJson{Id: utils.NewXid()}
			c.appendcmds(job, grp, v.(string))
			err := c.pushCmdGroup(ln, job, grp)
			ln++
			if err != nil {
				return err
			}
		case []interface{}:
			grp := &comm.CmdGroupJson{Id: utils.NewXid()}
			for _, v1 := range v.([]interface{}) {
				c.appendcmds(job, grp, fmt.Sprintf("%v", v1))
			}
			err := c.pushCmdGroup(ln, job, grp)
			ln++
			if err != nil {
				return err
			}
		case map[interface{}]interface{}:
			for k, v1 := range v.(map[interface{}]interface{}) {
				grp := &comm.CmdGroupJson{
					Id:   utils.NewXid(),
					Name: fmt.Sprintf("%v", k),
				}
				switch v1.(type) {
				case string:
					c.appendcmds(job, grp, fmt.Sprintf("%v", v1))
				case []interface{}:
					for _, v2 := range v1.([]interface{}) {
						c.appendcmds(job, grp, fmt.Sprintf("%v", v2))
					}
				}
				err := c.pushCmdGroup(ln, job, grp)
				ln++
				if err != nil {
					return err
				}
			}
		case map[string]interface{}:
			for k, v1 := range v.(map[string]interface{}) {
				grp := &comm.CmdGroupJson{
					Id:   utils.NewXid(),
					Name: k,
				}
				switch v1.(type) {
				case string:
					c.appendcmds(job, grp, fmt.Sprintf("%v", v1))
				case []interface{}:
					for _, v2 := range v1.([]interface{}) {
						c.appendcmds(job, grp, fmt.Sprintf("%v", v2))
					}
				}
				err := c.pushCmdGroup(ln, job, grp)
				ln++
				if err != nil {
					return err
				}
			}
		case map[string]string:
			for k, v1 := range v.(map[string]string) {
				grp := &comm.CmdGroupJson{
					Id:   utils.NewXid(),
					Name: k,
				}
				c.appendcmds(job, grp, fmt.Sprintf("%v", v1))
				err := c.pushCmdGroup(ln, job, grp)
				ln++
				if err != nil {
					return err
				}
			}
		case map[string][]string:
			for k, v1 := range v.(map[string][]string) {
				grp := &comm.CmdGroupJson{
					Id:   utils.NewXid(),
					Name: k,
				}
				for _, v2 := range v1 {
					c.appendcmds(job, grp, v2)
				}
				err := c.pushCmdGroup(ln, job, grp)
				ln++
				if err != nil {
					return err
				}
			}
		}

	}
	return nil
}

// 插入命令组到数据库
func (c *taskItem) pushCmdGroup(num int, job *comm.SyncJob, grp *comm.CmdGroupJson) error {
	db := comm2.DBMain.GetDB()
	e := &pipeline.TCmdGroup{
		Id:      grp.Id,
		BuildId: c.build.Id,
		JobId:   job.Job().Id,
		Name:    grp.Name,
		Num:     num,
		Status:  common.BUILD_STATUS_PENDING,
		Created: time.Now(),
	}
	_, err := db.Insert(e)
	if err != nil {
		return err
	}
	for i, cmd := range grp.Cmds {
		cmd.GroupId = e.Id
		cmd.BuildId = e.BuildId
		cmd.JobId = e.JobId
		cmd.Num = i
		cmd.Status = common.BUILD_STATUS_PENDING
		cmd.Created = e.Created
		_, err = db.Insert(cmd)
		if err != nil {
			return err
		}
	}
	return nil
}
